﻿using System;
using System.ComponentModel;

namespace Zive.Color {

    public enum XyzChannels { X, Y, Z }

    /// <summary>
    /// An implementation of the CIE 1931 XYZ color space.
    /// </summary>
    public class XyzColor: INotifyPropertyChanged {

        public XyzColor( ) { }

        public XyzColor( Illuminants illuminant ) {
            _illuminant = illuminant;
        }

        public XyzColor( double x, double y, double z ) {
            _x = x;
            _y = y;
            _z = z;
        }

        public XyzColor( Illuminants illuminant, double x, double y, double z ) {
            _illuminant = illuminant;
            _x = x;
            _y = y;
            _z = z;
        }

        public XyzColor( XyzColor other ) {
            _illuminant = other._illuminant;
            _x = other._x;
            _y = other._y;
            _z = other._z;
        }

        public XyzColor Clone( ) {
            return new XyzColor( this );
        }

        private Illuminants _illuminant = Illuminants.D65;
        /// <summary>Gets or sets the CIE standard illuminant and thus the white point.</summary>
        public Illuminants Illuminant {
            get { return _illuminant; }
            set {
                _illuminant = value;
                _OnPropertyChanged( "Illuminant" );
            }
        }

        private double _x;
        /// <summary>Gets or sets the value of the CIEXYZ X channel of the color.</summary>
        public double X {
            get { return _x; }
            set {
                _x = value;
                _OnPropertyChanged( "X" );
            }
        }

        private double _y;
        /// <summary>Gets or sets the value of the CIEXYZ Y channel of the color.</summary>
        public double Y {
            get { return _y; }
            set {
                _y = value;
                _OnPropertyChanged( "Y" );
            }
        }

        private double _z;
        /// <summary>Gets or sets the value of the CIEXYZ Z channel of the color.</summary>
        public double Z {
            get { return _z; }
            set {
                _z = value;
                _OnPropertyChanged( "Z" );
            }
        }

        public double this[ XyzChannels channel ] {
            get { return _getChannelFuncs[ (int) channel ]( this ); }
            set { _setChannelFuncs[ (int) channel ]( this, value ); }
        }

        private static readonly Func<XyzColor, double>[] _getChannelFuncs = {
            color => color._x,
            color => color._y,
            color => color._z,
        };

        private static readonly Action<XyzColor, double>[ ] _setChannelFuncs = {
            ( color, value ) => { color.X = value; },
            ( color, value ) => { color.Y = value; },
            ( color, value ) => { color.Z = value; },
        };

        #region Object Members

        public override string ToString( ) {
            return string.Format( "X={0:N6} Y={1:N6} Z={2:N6} illuminant={3:G}", _x, _y, _z, _illuminant );
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void _OnPropertyChanged( string propertyName ) {
            var handlers = PropertyChanged;
            if ( null != handlers ) {
                handlers( this, new PropertyChangedEventArgs( propertyName ) );
            }
        }

        #endregion

    }

}
